package com.lingyun.framework.elasticsearch;

import com.alibaba.fastjson.JSON;
import com.lingyun.common.exception.es.IndexException;
import com.lingyun.common.mapper.LyArticleMapper;
import com.lingyun.common.pojo.LyArticle;
import com.lingyun.common.utils.es.ElasticSearchUtils;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @author 没事别学JAVA
 * 2021/6/2 18:03
 */
@Service("elasticSearchService")
public class ElasticSearchServiceImpl implements ElasticSearchService, EsRepository {

    @Resource
    RestHighLevelClient restHighLevelClient;

    @Resource
    ElasticSearchUtils elasticSearchUtils;

    @Resource
    LyArticleMapper lyArticleMapper;


    // 索引名称
    private static final String ARTICLE_INDEX = "article";


    /**
     * 轮播图
     *
     * @return 轮播图集合
     */
    @Override
    public List<Map<String, Object>> shufflingFigure() throws IOException {
        //指定索引库
        SearchRequest searchRequest = new SearchRequest(ARTICLE_INDEX);

        //构造查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //精准匹配
        QueryBuilder builder = QueryBuilders.matchQuery("isShuffling", 1);
        searchSourceBuilder.query(builder);

        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));

        searchRequest.source(searchSourceBuilder);

        //发送请求
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        ArrayList<Map<String, Object>> list = new ArrayList<>();
        SearchHit[] hits = searchResponse.getHits().getHits();
        for (SearchHit hit : hits) {
            list.add(hit.getSourceAsMap());
        }
        return list;
    }

    /**
     *
     * 推广内容
     *
     * @return 推广集合
     */
    @Override
    public List<Map<String, Object>> queryPopularize() throws IOException {
        //指定索引库
        SearchRequest searchRequest = new SearchRequest(ARTICLE_INDEX);

        //构造查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //精准匹配
        QueryBuilder builder = QueryBuilders.matchQuery("isPopularize", 1);
        searchSourceBuilder.query(builder);
        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));

        searchRequest.source(searchSourceBuilder);

        //发送请求
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        ArrayList<Map<String, Object>> list = new ArrayList<>();
        SearchHit[] hits = searchResponse.getHits().getHits();
        for (SearchHit hit : hits) {
            list.add(hit.getSourceAsMap());
        }
        return list;
    }

    @Override
    public Map<String,Object> queryArticle(Long articleId) throws IOException {
        GetRequest request = new GetRequest(ARTICLE_INDEX ,  Long.toString(articleId)) ;
        GetResponse response = restHighLevelClient.get(request , RequestOptions.DEFAULT);
        return response.getSource();
    }

    /**
     *
     * @param keyword
     * @param current  当前页
     * @param size 页大小
     * @return
     * @throws IOException
     */
    @Override
    public List<Map<String, Object>> search(String keyword, Integer current, Integer size) throws IOException, IndexException {
        //判断索引是否存在
        if(!elasticSearchUtils.isExistsIndex(ARTICLE_INDEX)){
            throw new IndexException();
        };

        //当前页不能小于1，如果小于1 ，就让他等于1
        if (current <= 1) {
            current = 1;
        }

        //指定索引库
        SearchRequest searchRequest = new SearchRequest(ARTICLE_INDEX);
        //构造查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();


        //分页 from = (当前页-1)*一页显示条数
        searchSourceBuilder.from((current - 1) * size); // 开始位置
        searchSourceBuilder.size(size);    // 页大小


        //精准匹配
        QueryBuilder builder = QueryBuilders.matchQuery("articleName", keyword);
        searchSourceBuilder.query(builder);
        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));


        //高亮显示
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.requireFieldMatch(false)
                .field("articleName")
                .preTags("<a style=\"color:red\">").postTags("</a>");


        //执行搜索
        if(keyword!=null){
            searchSourceBuilder.highlighter(highlightBuilder);
            searchRequest.source(searchSourceBuilder);
        }else{
            searchRequest.source();
        }

        //发送请求
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        ArrayList<Map<String, Object>> list = new ArrayList<>();
        SearchHit[] hits = searchResponse.getHits().getHits();
        for (SearchHit hit : hits) {

            //获取高亮
            String articleName = hit.getHighlightFields().get("articleName").getFragments()[0].string();
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            sourceAsMap.put("articleName",articleName);

            list.add(sourceAsMap);
        }
        return list;
    }



    @Override
    public Integer reset(){

        //1.创建批量导入数据
        BulkRequest bulkRequest = new BulkRequest ();
        List<LyArticle> lyArticleList= lyArticleMapper.resetList();

        lyArticleList.forEach (lyArticle -> {
            bulkRequest.add(new IndexRequest(ARTICLE_INDEX)
                    .id(String.valueOf(lyArticle.getArticleId()))
                    .source(JSON.toJSONString(lyArticle), XContentType.JSON)
            );
        });

        //4.执行请求
        BulkResponse bulkResponse = null;
        try {
            bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //5.响应 判断是否执行成功
        RestStatus status = bulkResponse.status();
        return status.getStatus ();
    }






    /**
     * 根据给定的实体和字段，查找与之相似的文档，并返回分页结果。
     *
     * @param entity   作为相似性搜索的参考实体
     * @param fields   指定在哪些字段上进行相似性搜索
     * @param pageable 分页信息，用于控制返回结果的分页
     * @return         返回与实体相似的文档分页结果
     */
    @Override
    public Page<LyArticle> searchSimilar(LyArticle entity, String[] fields, Pageable pageable) {
        return null;
    }

    /**
     * 返回所有文档，并根据指定的排序规则进行排序。
     *
     * @param sort 排序规则，可以指定按某个字段升序或降序排列
     * @return     返回排序后的所有文档
     */
    @Override
    public Iterable<LyArticle> findAll(Sort sort) {
        return null;
    }

    /**
     * 返回所有文档，并根据分页信息进行分页。
     *
     * @param pageable 分页信息，包含页码和每页大小
     * @return         返回分页后的所有文档
     */
    @Override
    public Page<LyArticle> findAll(Pageable pageable) {
        return null;
    }

    /**
     * 保存或更新一个实体到 Elasticsearch 中。
     *
     * @param entity 要保存或更新的实体
     * @param <S>    实体类型
     * @return       返回保存或更新后的实体
     */
    @Override
    public <S extends LyArticle> S save(S entity) {
        return null;
    }

    /**
     * 批量保存或更新多个实体到 Elasticsearch 中。
     *
     * @param entities 要保存或更新的实体集合
     * @param <S>      实体类型
     * @return         返回保存或更新后的实体集合
     */
    @Override
    public <S extends LyArticle> Iterable<S> saveAll(Iterable<S> entities) {
        return null;
    }

    /**
     * 根据 ID 查找一个文档。
     *
     * @param aLong 文档的 ID
     * @return      返回查找到的文档，如果不存在则返回空
     */
    @Override
    public Optional<LyArticle> findById(Long aLong) {
        return Optional.empty();
    }

    /**
     * 检查指定 ID 的文档是否存在。
     *
     * @param aLong 文档的 ID
     * @return      如果文档存在则返回 true，否则返回 false
     */
    @Override
    public boolean existsById(Long aLong) {
        return false;
    }

    /**
     * 返回所有文档。
     *
     * @return 返回所有文档的集合
     */
    @Override
    public Iterable<LyArticle> findAll() {
        return null;
    }

    /**
     * 根据一组 ID 查找多个文档。
     *
     * @param longs 文档 ID 的集合
     * @return      返回查找到的文档集合
     */
    @Override
    public Iterable<LyArticle> findAllById(Iterable<Long> longs) {
        return null;
    }

    /**
     * 返回文档的总数。
     *
     * @return 返回文档的总数
     */
    @Override
    public long count() {
        return 0;
    }

    /**
     * 根据 ID 删除一个文档。
     *
     * @param aLong 文档的 ID
     */
    @Override
    public void deleteById(Long aLong) {
    }

    /**
     * 删除指定的实体。
     *
     * @param entity 要删除的实体
     */
    @Override
    public void delete(LyArticle entity) {
    }

    /**
     * 根据一组 ID 删除多个文档。
     *
     * @param longs 文档 ID 的集合
     */
    @Override
    public void deleteAllById(Iterable<? extends Long> longs) {
    }

    /**
     * 删除指定的多个实体。
     *
     * @param entities 要删除的实体集合
     */
    @Override
    public void deleteAll(Iterable<? extends LyArticle> entities) {
    }

    /**
     * 删除所有文档。
     */
    @Override
    public void deleteAll() {
    }


}
